var __createBinding =
    (this && this.__createBinding) ||
    (Object.create
        ? (o, m, k, k2) => {
              if (k2 === undefined) k2 = k;
              var desc = Object.getOwnPropertyDescriptor(m, k);
              if (
                  !desc ||
                  ("get" in desc
                      ? !m.__esModule
                      : desc.writable || desc.configurable)
              ) {
                  desc = { enumerable: true, get: () => m[k] };
              }
              Object.defineProperty(o, k2, desc);
          }
        : (o, m, k, k2) => {
              if (k2 === undefined) k2 = k;
              o[k2] = m[k];
          });
var __setModuleDefault =
    (this && this.__setModuleDefault) ||
    (Object.create
        ? (o, v) => {
              Object.defineProperty(o, "default", {
                  enumerable: true,
                  value: v,
              });
          }
        : (o, v) => {
              o.default = v;
          });
var __importStar =
    (this && this.__importStar) ||
    ((mod) => {
        if (mod?.__esModule) return mod;
        var result = {};
        if (mod != null)
            for (var k in mod)
                if (k !== "default" && Object.hasOwn(mod, k))
                    __createBinding(result, mod, k);
        __setModuleDefault(result, mod);
        return result;
    });
var __importDefault =
    (this && this.__importDefault) ||
    ((mod) => (mod?.__esModule ? mod : { default: mod }));
Object.defineProperty(exports, "__esModule", { value: true });
exports.DeadBranchRemover = void 0;
const t = __importStar(require("@babel/types"));
const traverse_1 = __importDefault(require("@babel/traverse"));
const transformation_1 = require("../transformation");
class DeadBranchRemover extends transformation_1.Transformation {
    /**
     * Executes the transformation.
     * @param log The log function.
     */
    execute(_log) {
        const self = this;
        (0, traverse_1.default)(this.ast, {
            IfStatement(path) {
                if (self.isSemiLiteral(path.node.test)) {
                    if (self.isTruthy(path.node.test)) {
                        const statements = t.isBlockStatement(
                            path.node.consequent,
                        )
                            ? path.node.consequent.body
                            : [path.node.consequent];
                        path.replaceWithMultiple(statements);
                        self.setChanged();
                    } else {
                        if (path.node.alternate) {
                            if (t.isBlockStatement(path.node.alternate)) {
                                path.replaceWithMultiple(
                                    path.node.alternate.body,
                                );
                            } else {
                                path.replaceWith(path.node.alternate);
                            }
                        } else {
                            path.remove();
                        }
                        self.setChanged();
                    }
                }
            },
            ConditionalExpression(path) {
                // simplify expressions in form (true ? ... : ...)
                if (self.isSemiLiteral(path.node.test)) {
                    const replacement = self.isTruthy(path.node.test)
                        ? path.node.consequent
                        : path.node.alternate;
                    path.replaceWith(replacement);
                    self.setChanged();
                }
                // simplify expressions in form (a ? true : false)
                else if (
                    t.isBooleanLiteral(path.node.consequent) &&
                    t.isBooleanLiteral(path.node.alternate)
                ) {
                    const consequent = path.node.consequent.value;
                    const alternate = path.node.alternate.value;
                    let replacement;
                    if (consequent && !alternate) {
                        replacement = t.unaryExpression(
                            "!",
                            t.unaryExpression("!", path.node.test),
                        );
                    } else if (!consequent && alternate) {
                        replacement = t.unaryExpression("!", path.node.test);
                    } else if (consequent && alternate) {
                        replacement = t.sequenceExpression([
                            path.node.test,
                            t.booleanLiteral(true),
                        ]);
                    } else {
                        replacement = t.sequenceExpression([
                            path.node.test,
                            t.booleanLiteral(false),
                        ]);
                    }
                    path.replaceWith(replacement);
                    self.setChanged();
                }
            },
        });
        return this.hasChanged();
    }
    /**
     * Returns whether a node is a literal or can be treated as one in the context of a test expression.
     * @param node The node.
     * @returns Whether.
     */
    isSemiLiteral(node) {
        return (
            t.isLiteral(node) ||
            t.isArrayExpression(node) ||
            t.isObjectExpression(node)
        );
    }
    /**
     * Returns whether a literal node is truthy.
     * @param literal The literal node.
     * @returns Whether.
     */
    isTruthy(literal) {
        return t.isBooleanLiteral(literal) ||
            t.isNumericLiteral(literal) ||
            t.isStringLiteral(literal)
            ? !!literal.value
            : true;
    }
}
exports.DeadBranchRemover = DeadBranchRemover;
DeadBranchRemover.properties = {
    key: "deadBranchRemoval",
    rebuildScopeTree: true,
};
